package cn.stimd.spring.beans.factory.support;

import cn.stimd.spring.beans.BeanWrapper;
import cn.stimd.spring.beans.BeanWrapperImpl;
import cn.stimd.spring.beans.BeansException;
import cn.stimd.spring.beans.factory.config.DependencyDescriptor;
import org.springframework.core.MethodParameter;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * 通过参数匹配的方式解析构造器和工厂方法，工厂方法的参数也是用于创建对象的，因此可以间接认为在解析构造器。
 * <p>
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class ConstructorResolver {
    private final AbstractAutowireCapableBeanFactory beanFactory;

    public ConstructorResolver(AbstractAutowireCapableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }


    /**
     * 自动装配构造器并创建Bean
     */
    public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, Constructor<?>[] chosenCtors) {
        //1. 准备工作
        BeanWrapperImpl bw = new BeanWrapperImpl();
        Constructor<?>[] candidates = chosenCtors;
        if (candidates == null) {
            candidates = mbd.getBeanClass().getDeclaredConstructors();
        }

        //对候选的构造器进行排序，优先选择公共的以及参数多的构造器
        AutowireUtils.sortConstructors(candidates);
        Constructor<?> constructorToUse = null;     //用于创建对象的构造器
        Object[] argsToUse = null;                  //用于创建对象的构造器参数
        Exception exception = null;

        //2. 对构造器进行解析，如果存在多个构造器，则找出最适合的一个
        for (Constructor<?> candidate : candidates) {
            Class<?>[] paramTypes = candidate.getParameterTypes();
            Object[] args;
            try {
                args = createArgumentArray(beanName, paramTypes, candidate);
            }catch (Exception e){
                //解析依赖时，如果Spring中不存在对应的Bean，将会抛出异常，这里不处理，继续遍历其他候选项
                exception = e;
                continue;
            }

            //找出最适合的构造器（简化实现，以第一个构造器作为最终构造器）
            if(constructorToUse == null) {
                constructorToUse = candidate;
                argsToUse = args;   //源码无，确保参数与构造器对应
            }
        }

        if (constructorToUse == null) {
            throw new BeansException("解析Bean[" + beanName + "]的构造器失败", exception);
        }

        //3. 反射构造器创建实例
        Object instance = this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, constructorToUse, argsToUse);
        bw.setBeanInstance(instance);
        return bw;
    }


    /**
     * 使用工厂方法实例化Bean
     * 1. 只处理实例方法，静态方法不考虑
     * 2. 重载的工厂方法不考虑
     */
    public BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd) {
        try {
            BeanWrapperImpl bw = new BeanWrapperImpl();
            String factoryBeanName = mbd.getFactoryBeanName();
            Object factoryBean = null;
            Class<?> factoryClass = null;

            //1. 获取工厂方法声明类的实例
            //factoryBeanName属性不为空，说明是实例方法（不处理静态工厂方法）
            if(factoryBeanName != null){
                if (factoryBeanName.equals(beanName)) {
                    throw new BeansException("正在创建的Bean[" + beanName + "]不能与工厂方法声明类 [" + factoryBeanName +"]的名称相同");
                }

                factoryBean = this.beanFactory.getBean(factoryBeanName);

                if (factoryBean == null) {
                    throw new BeansException("工厂方法声明类 [" + factoryBeanName + "]的实例不存在");
                }

                if(this.beanFactory.containsSingleton(beanName)){
                    throw new IllegalStateException("正在创建的Bean[" + beanName + "]的实例已存在");
                }

                factoryClass = factoryBean.getClass();
            }

            //2. 获取工厂方法（不考虑多个重载方法的情况，简单认为只有一个工厂方法）
            Method factoryMethodToUse = (Method)mbd.resolvedConstructorOrFactoryMethod;
            if(factoryMethodToUse == null){
                Method[] rawCandidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
                for (Method candidate : rawCandidates) {
                    if (mbd.isFactoryMethod(candidate)) {
                        factoryMethodToUse = candidate;
                        mbd.resolvedConstructorOrFactoryMethod = candidate;
                        break;
                    }
                }
            }

            //3. 如果工厂方法存在参数，则需要解析依赖项
            Object[] argsToUse = null;
            Class<?>[] paramTypes = factoryMethodToUse.getParameterTypes();
            if(paramTypes.length > 0){
                argsToUse = createArgumentArray(beanName, paramTypes, factoryMethodToUse);
            }

            //4. 实例化对象
            Object instance = this.beanFactory.getInstantiationStrategy().instantiate(beanName, factoryBean, factoryMethodToUse, argsToUse);
            bw.setBeanInstance(instance);
            return bw;
        }catch (Exception e){
            throw new BeansException("Bean[" + mbd.getFactoryBeanName() + "]的工厂方法[" + mbd.getFactoryMethodName() + "]实例化失败", e);
        }
    }


    private Object[] createArgumentArray(String beanName, Class<?>[] paramTypes, Object methodOrCtor){
        Object[] arguments = new Object[paramTypes.length];

        //遍历方法参数，依次进行解析
        for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
            MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex);
            DependencyDescriptor descriptor = new DependencyDescriptor(methodParam, true);
            //解析参数的依赖项
            Object autowiredArgument = this.beanFactory.resolveDependency(descriptor, beanName);
            arguments[paramIndex] = autowiredArgument;
        }
        return arguments;
    }
}
